#include <ptgui/dc.h>
#include <ptgui/system.h>
#include <ptgui/region.h>

#include <string.h>	/* for strlen */
#include <stdlib.h>	/* fir qsort  */

/* for sin/cos etc */
#include <math.h>
#ifndef M_PI
#define M_PI	3.14159265358979323846
#endif

ptgui_gui_content_t gui_content;

void ptgui_gc_system_init(void)
{
	/* init gc */
	gui_content.foreground = COLOR_DEFAULT_BACKGROUND;
	gui_content.background = COLOR_DEFAULT_FOREGROUND;
	gui_content.font = ptgui_font_default();
	gui_content.text_align = PTGUI_ALIGN_LEFT | PTGUI_ALIGN_TOP;
}

void ptgui_dc_destory(struct ptgui_dc *dc)
{
	if (dc == RT_NULL) {
		return;
	}

	dc->fini(dc);
	ptgui_free(dc);
}

/*
 * draw a point on dc
 */
void ptgui_dc_draw_point(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y)
{
	if (dc == RT_NULL) {
		return;
	}

	dc->draw_point(dc, x, y);
}

/*
 * draw a vertical line on dc
 */
void ptgui_dc_draw_vline(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y1, rt_int16_t y2)
{
	if (dc == RT_NULL) {
		return;
	}

	dc->draw_vline(dc, x, y1, y2);
}

/*
 * draw a horizontal line on dc
 */
void ptgui_dc_draw_hline(struct ptgui_dc *dc, rt_int16_t x1, rt_int16_t x2, rt_int16_t y)
{
	if (dc == RT_NULL) {
		return;
	}

	dc->draw_hline(dc, x1, x2, y);
}

void ptgui_dc_draw_line(struct ptgui_dc *dc, rt_int16_t x1, rt_int16_t y1, rt_int16_t x2, rt_int16_t y2)
{
	if (dc == RT_NULL) {
		return;
	}

	if (y1 == y2) {
		ptgui_dc_draw_hline(dc, x1, x2, y1);
	}
	else if (x1 == x2) {
		ptgui_dc_draw_vline(dc, x1, y1, y2);
	}
	else {
		rt_int16_t dx,
				   dy,
				   sdx,
				   sdy,
				   dxabs,
				   dyabs,
				   x,
				   y,
				   px,
				   py;
		register rt_base_t i;
		/* ptgui_rect_t rect; */

		dx = x2 - x1;		/* the horizontal distance of the line */
		dy = y2 - y1;		/* the vertical distance of the line */

#define ptgui_sgn(x) ((x<0)?-1:((x>0)?1:0))	/* macro to return the sign of a number */
#define ptgui_abs(x) ((x)>=0? (x):-(x))		/* macro to return the absolute value */

		dxabs = ptgui_abs(dx);
		dyabs = ptgui_abs(dy);
		sdx = ptgui_sgn(dx);
		sdy = ptgui_sgn(dy);
		x = dyabs >> 1;
		y = dxabs >> 1;
		px = x1;
		py = y1;

		if (dxabs >= dyabs)	/* the line is more horizontal than vertical */ {
			for (i = 0; i < dxabs; i++) {
				y += dyabs;
				if (y >= dxabs) {
					y -= dxabs;
					py += sdy;
				}
				px += sdx;

				/* draw this point */
				dc->draw_point(dc, px, py);
			}
		}
		else				/* the line is more vertical than horizontal */ {
			for (i = 0; i < dyabs; i++) {
				x += dxabs;
				if (x >= dyabs) {
					x -= dyabs;
					px += sdx;
				}
				py += sdy;

				/* draw this point */
				dc->draw_point(dc, px, py);
			}
		}
	}
}

void ptgui_dc_draw_rect(struct ptgui_dc *dc, struct ptgui_rect *rect)
{
	ptgui_dc_draw_hline(dc, rect->x1, rect->x2, rect->y1);
	ptgui_dc_draw_hline(dc, rect->x1, rect->x2, rect->y2 - 1);

	ptgui_dc_draw_vline(dc, rect->x1, rect->y1, rect->y2);
	ptgui_dc_draw_vline(dc, rect->x2 - 1, rect->y1, rect->y2);
}

void ptgui_dc_draw_round_rect(struct ptgui_dc *dc, struct ptgui_rect *rect)
{
	rt_int16_t r = 3;

	ptgui_dc_draw_arc(dc, rect->x1 + r, rect->y1 + r, r, 180, 270);
	ptgui_dc_draw_arc(dc, rect->x2 - r, rect->y1 + r, r, 270, 360);
	ptgui_dc_draw_arc(dc, rect->x1 + r, rect->y2 - r, r, 90, 180);
	ptgui_dc_draw_arc(dc, rect->x2 - r, rect->y2 - r, r, 0, 90);

	ptgui_dc_draw_hline(dc, rect->x1 + r, rect->x2 - r, rect->y1);
	ptgui_dc_draw_hline(dc, rect->x1 + r, rect->x2 - r, rect->y2);
	ptgui_dc_draw_vline(dc, rect->x1, rect->y1 + r, rect->y2 - r);
	ptgui_dc_draw_vline(dc, rect->x2, rect->y1 + r, rect->y2 - r);
}

void ptgui_dc_fill_rect(struct ptgui_dc *dc, struct ptgui_rect *rect)
{
	if (dc == RT_NULL) {
		return;
	}

	dc->fill_rect(dc, rect);
}

void ptgui_dc_blit(struct ptgui_dc *dc, struct ptgui_point *dc_point, struct ptgui_dc *dest, ptgui_rect_t *rect)
{
	if (dc == RT_NULL || dest == RT_NULL || rect == RT_NULL) {
		return;
	}

	dc->blit(dc, dc_point, dest, rect);
}

void ptgui_dc_draw_text(struct ptgui_dc *dc, const char *text, struct ptgui_rect *rect)
{
	rt_uint32_t len;
	struct ptgui_font *font;
#ifdef PTGUI_USING_FONTHZ
	struct ptgui_font *gb2312_font;
#endif
	struct ptgui_rect text_rect;

	RT_ASSERT(dc != RT_NULL);

	font = ptgui_dc_get_font(dc);
	if (font == RT_NULL) {
		/* use system default font */
		font = ptgui_font_default();
	}

#ifdef PTGUI_USING_FONTHZ
	gb2312_font = ptgui_font_refer("hz", font->height);
	if (gb2312_font == RT_NULL) {
		gb2312_font = ptgui_font_refer("hz", 16);
	}
#endif

	/* text align */
	ptgui_font_get_metrics(font, text, &text_rect);
	ptgui_rect_moveto_align(rect, &text_rect, ptgui_dc_get_textalign(dc));

#ifdef PTGUI_USING_FONTHZ
	while (*text) {
		len = 0;
		while (*(text + len) < 0x80 && *(text + len))
			len ++;
		if (len > 0) {
			ptgui_font_draw(font, dc, text, len, &text_rect);

			text += len;
		}

		len = 0;
		while (*(text + len) > 0x80)
			len ++;
		if (len > 0) {
			ptgui_font_draw(gb2312_font, dc, text, len, &text_rect);

			text += len;
		}
	}

	ptgui_font_derefer(gb2312_font);
#else
	len = strlen((const char*)text);
	ptgui_font_draw(font, dc, text, len, &text_rect);
#endif
}

void ptgui_dc_draw_byte(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y, rt_int16_t h, const rt_uint8_t *data)
{
	rt_int16_t i,
			   k;

	/* draw byte */
	for (i = 0; i < h; i ++) {
		for (k = 0; k < 8; k++) {
			if (((data[i] >> (7 - k)) & 0x01) != 0) {
				ptgui_dc_draw_point(dc, x + k, y + i);
			}
		}
	}
}

void ptgui_dc_draw_word(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y, rt_int16_t h, const rt_uint8_t *data)
{
	rt_int16_t i,
			   j,
			   k;

	/* draw word */
	for (i = 0; i < h; i ++) {
		for (j = 0; j < 2; j++)
			for (k = 0; k < 8; k++) {
				if (((data[i * 2 + j] >> (7 - k)) & 0x01) != 0) {
					ptgui_dc_draw_point(dc, x + 8 * j + k, y + i);
				}
			}
	}
}

void ptgui_dc_set_color(struct ptgui_dc *dc, ptgui_color_t color)
{
	if (dc != RT_NULL) {
		dc->set_color(dc, color);
	}
}

void ptgui_dc_set_bg_color(struct ptgui_dc *dc, ptgui_color_t color)
{
	if (dc != RT_NULL) {
		dc->set_bg_color(dc, color);
	}
}

ptgui_color_t ptgui_dc_get_color(struct ptgui_dc *dc)
{
	if (dc != RT_NULL) {
		return dc->get_color(dc);
	}

	return COLOR_WHITE;
}

void ptgui_dc_set_font(struct ptgui_dc *dc, ptgui_font_t *font)
{
	if (dc != RT_NULL) {
		dc->set_font(dc, font);
	}
}

ptgui_font_t * ptgui_dc_get_font(struct ptgui_dc *dc)
{
	if (dc != RT_NULL) {
		return dc->get_font(dc);
	}

	return RT_NULL;
}

void ptgui_dc_set_textalign(struct ptgui_dc *dc, rt_int32_t align)
{
	if (dc != RT_NULL) {
		dc->set_textalign(dc, align);
	}
}

rt_int32_t ptgui_dc_get_textalign(struct ptgui_dc *dc)
{
	if (dc != RT_NULL) {
		return dc->get_textalign(dc);
	}

	return PTGUI_ALIGN_NOT;
}

rt_bool_t ptgui_dc_get_visible(struct ptgui_dc *dc)
{
	if (dc != RT_NULL) {
		return dc->get_visible(dc);
	}

	return RT_FALSE;
}

void ptgui_dc_draw_shaded_rect(struct ptgui_dc *dc, ptgui_rect_t *rect, ptgui_color_t c1, ptgui_color_t c2)
{
	RT_ASSERT(dc != RT_NULL);

	ptgui_dc_set_color(dc, c1);
	ptgui_dc_draw_vline(dc, rect->x1, rect->y1, rect->y2);
	ptgui_dc_draw_hline(dc, rect->x1 + 1, rect->x2, rect->y1);

	ptgui_dc_set_color(dc, c2);
	ptgui_dc_draw_vline(dc, rect->x2, rect->y1, rect->y2);
	ptgui_dc_draw_hline(dc, rect->x1, rect->x2 + 1, rect->y2);
}

void ptgui_dc_draw_border(struct ptgui_dc *dc, ptgui_rect_t *rect, rt_int16_t flag)
{
	ptgui_rect_t r;
	ptgui_color_t color;

	if (dc == RT_NULL) {
		return ;
	}

	/* save old color */
	color = ptgui_dc_get_color(dc);

	r = *rect;
	switch (flag) {
	case PTGUI_BORDER_RAISE:
		ptgui_dc_draw_shaded_rect(dc, &r, COLOR_HIGH_LIGHT, COLOR_BLACK);
		ptgui_rect_inflate(&r, -1);
		ptgui_dc_draw_shaded_rect(dc, &r, COLOR_LIGHT_GREY, COLOR_DARK_GREY);
		break;

	case PTGUI_BORDER_SUNKEN:
		ptgui_dc_draw_shaded_rect(dc, &r, COLOR_DARK_GREY, COLOR_HIGH_LIGHT);
		ptgui_rect_inflate(&r, -1);
		ptgui_dc_draw_shaded_rect(dc, &r, COLOR_BLACK, COLOR_LIGHT_GREY);
		break;

	case PTGUI_BORDER_BOX:
		ptgui_dc_draw_shaded_rect(dc, &r, COLOR_DARK_GREY, COLOR_HIGH_LIGHT);
		ptgui_rect_inflate(&r, -1);
		ptgui_dc_draw_shaded_rect(dc, &r, COLOR_HIGH_LIGHT, COLOR_DARK_GREY);
		break;

	case PTGUI_BORDER_STATIC:
		ptgui_dc_draw_shaded_rect(dc, &r, COLOR_DARK_GREY, COLOR_HIGH_LIGHT);
		break;

	case PTGUI_BORDER_EXTRA:
		ptgui_dc_set_color(dc, COLOR_LIGHT_GREY);
		ptgui_dc_draw_rect(dc, &r);
		break;

	case PTGUI_BORDER_SIMPLE:
		ptgui_dc_set_color(dc, COLOR_BLACK);
		ptgui_dc_draw_rect(dc, &r);
		break;

	default:
		break;
	}

	/* restore color */
	ptgui_dc_set_color(dc, color);
}

void ptgui_dc_draw_horizontal_line(struct ptgui_dc *dc, rt_int16_t x1, rt_int16_t x2, rt_int16_t y)
{
	ptgui_color_t color;

	if (dc == RT_NULL) {
		return ;
	}

	/* save old color */
	color = ptgui_dc_get_color(dc);

	ptgui_dc_set_color(dc, COLOR_DARK_GREY);
	ptgui_dc_draw_hline(dc, x1, x2, y);

	y ++;

	ptgui_dc_set_color(dc, COLOR_HIGH_LIGHT);
	ptgui_dc_draw_hline(dc, x1, x2, y);

	/* restore color */
	ptgui_dc_set_color(dc, color);
}

void ptgui_dc_draw_vertical_line(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y1, rt_int16_t y2)
{
	ptgui_color_t color;

	if (dc == RT_NULL) {
		return ;
	}

	/* save old color */
	color = ptgui_dc_get_color(dc);

	ptgui_dc_set_color(dc, COLOR_DARK_GREY);
	ptgui_dc_draw_hline(dc, x, y1, y2);

	x ++;

	ptgui_dc_set_color(dc, COLOR_HIGH_LIGHT);
	ptgui_dc_draw_hline(dc, x, y1, y2);

	/* restore color */
	ptgui_dc_set_color(dc, color);
}

void ptgui_dc_draw_arrow(struct ptgui_dc *dc, ptgui_rect_t *rect, rt_int16_t kind)
{
	rt_int16_t i;
	rt_int16_t x1,
			   y1,
			   x2,
			   y2;
	ptgui_rect_t r =
	{
		0,
		0,
		0,
		0
	};

	static const rt_uint8_t ARROW_WIDTH = 7;
	static const rt_uint8_t ARROW_LENGTH = 4;

	x1 = y1 = 0;

	switch (kind) {
	case PTGUI_ARRAW_UP:
	case PTGUI_ARRAW_DOWN:
		r.x2 = ARROW_WIDTH;
		r.y2 = ARROW_LENGTH;
		break;

	case PTGUI_ARRAW_LEFT:
	case PTGUI_ARRAW_RIGHT:
		r.x2 = ARROW_LENGTH;
		r.y2 = ARROW_WIDTH;
		break;
	}
	ptgui_rect_moveto_align(rect, &r, PTGUI_ALIGN_CENTER_HORIZONTAL | PTGUI_ALIGN_CENTER_VERTICAL);

	switch (kind) {
	case PTGUI_ARRAW_UP:
		x1 = r.x1 + (ARROW_WIDTH - 1) / 2;;
		y1 = r.y1;
		break;
	case PTGUI_ARRAW_DOWN:
		x1 = r.x1 + (ARROW_WIDTH - 1) / 2;
		y1 = r.y1 + ARROW_LENGTH - 1;
		break;
	case PTGUI_ARRAW_LEFT:
		x1 = r.x1;
		y1 = r.y1 + (ARROW_WIDTH - 1) / 2;
		break;
	case PTGUI_ARRAW_RIGHT:
		x1 = r.x1 + ARROW_LENGTH - 1;
		y1 = r.y1 + (ARROW_WIDTH - 1) / 2;
		break;
	default:
		return;
	}
	x2 = x1;
	y2 = y1;

	for (i = 0; i < ARROW_LENGTH; i++) {
		ptgui_dc_draw_line(dc, x1, y1, x2, y2);

		switch (kind) {
		case PTGUI_ARRAW_UP:
			x1 --;
			x2 ++;
			y1 ++;
			y2 ++;
			break;

		case PTGUI_ARRAW_DOWN:
			x1 --;
			x2 ++;
			y1 --;
			y2 --;
			break;

		case PTGUI_ARRAW_LEFT:
			y1 --;
			y2 ++;
			x1 ++;
			x2 ++;
			break;

		case PTGUI_ARRAW_RIGHT:
			y1 --;
			y2 ++;
			x1 --;
			x2 --;
			break;
		}
	}
}

void ptgui_dc_draw_polygon(struct ptgui_dc *dc, const rt_int16_t *vx, const rt_int16_t *vy, rt_int16_t count)
{
	rt_int16_t i;
	const rt_int16_t *x1,
					  *y1,
					  *x2,
					  *y2;

	/*
	* Sanity check
	*/
	if (count < 3) {
		return;
	}

	/*
	* Pointer setup
	*/
	x1 = x2 = vx;
	y1 = y2 = vy;
	x2++;
	y2++;

	/*
	* Draw
	*/
	for (i = 1; i < count; i++) {
		ptgui_dc_draw_line(dc, *x1, *y1, *x2, *y2);
		x1 = x2;
		y1 = y2;
		x2++;
		y2++;
	}
	ptgui_dc_draw_line(dc, *x1, *y1, *vx, *vy);
}

static int _int_compare(const void *a, const void *b)
{
	return (*(const rt_int16_t *)a) - (*(const rt_int16_t *)b);
}

void ptgui_dc_fill_polygon(struct ptgui_dc *dc, const rt_int16_t *vx, const rt_int16_t *vy, rt_int16_t count)
{
	rt_int16_t i;
	rt_int16_t y,
			   xa,
			   xb;
	rt_int16_t miny,
			   maxy;
	rt_int16_t x1,
			   y1;
	rt_int16_t x2,
			   y2;
	rt_int16_t ind1,
			   ind2;
	rt_int16_t ints;
	rt_int16_t *poly_ints = RT_NULL;

	/*
	* Sanity check number of edges
	*/
	if (count < 3) {
		return;
	}

	/*
	* Allocate temp array, only grow array
	*/
	poly_ints = (rt_int16_t *)rt_malloc(sizeof(rt_int16_t) * count);
	if (poly_ints == RT_NULL) {
		return ;
	} /* no memory, failed */

	/*
	 * Determine Y maximal
	 */
	miny = vy[0];
	maxy = vy[0];
	for (i = 1; (i < count); i++) {
		if (vy[i] < miny) {
			miny = vy[i];
		}
		else if (vy[i] > maxy) {
			maxy = vy[i];
		}
	}

	/*
	* Draw, scanning y
	*/
	for (y = miny; (y <= maxy); y++) {
		ints = 0;
		for (i = 0; (i < count); i++) {
			if (!i) {
				ind1 = count - 1;
				ind2 = 0;
			}
			else {
				ind1 = i - 1;
				ind2 = i;
			}
			y1 = vy[ind1];
			y2 = vy[ind2];
			if (y1 < y2) {
				x1 = vx[ind1];
				x2 = vx[ind2];
			}
			else if (y1 > y2) {
				y2 = vy[ind1];
				y1 = vy[ind2];
				x2 = vx[ind1];
				x1 = vx[ind2];
			}
			else {
				continue;
			}

			if (((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2))) {
				poly_ints[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1);
			}
		}

		qsort(poly_ints, ints, sizeof(rt_int16_t), _int_compare);

		for (i = 0; (i < ints); i += 2) {
			xa = poly_ints[i] + 1;
			xa = (xa >> 16) + ((xa & 32768) >> 15);
			xb = poly_ints[i + 1] - 1;
			xb = (xb >> 16) + ((xb & 32768) >> 15);
			ptgui_dc_draw_hline(dc, xa, xb, y);
		}
	}
}

#if 1
void ptgui_dc_draw_circle(struct ptgui_dc *dc, rt_int16_t xCenter, rt_int16_t yCenter, rt_int16_t radius)
{
	rt_int16_t x = 0;
	rt_int16_t y = radius;
	rt_int16_t p = 1 - radius;

	ptgui_dc_draw_point(dc, xCenter + x, yCenter + y);
	ptgui_dc_draw_point(dc, xCenter - x, yCenter + y);
	ptgui_dc_draw_point(dc, xCenter + x, yCenter - y);
	ptgui_dc_draw_point(dc, xCenter - x, yCenter - y);

	ptgui_dc_draw_point(dc, xCenter + y, yCenter + x);
	ptgui_dc_draw_point(dc, xCenter - y, yCenter + x);
	ptgui_dc_draw_point(dc, xCenter + y, yCenter - x);
	ptgui_dc_draw_point(dc, xCenter - y, yCenter - x);

	while (x < y) {
		x++;
		if (p < 0) {
			p += 2 * x + 1;
		}
		else {
			y--;
			p += 2 * (x - y) + 1;
		}
		ptgui_dc_draw_point(dc, xCenter + x, yCenter + y);
		ptgui_dc_draw_point(dc, xCenter - x, yCenter + y);
		ptgui_dc_draw_point(dc, xCenter + x, yCenter - y);
		ptgui_dc_draw_point(dc, xCenter - x, yCenter - y);

		ptgui_dc_draw_point(dc, xCenter + y, yCenter + x);
		ptgui_dc_draw_point(dc, xCenter - y, yCenter + x);
		ptgui_dc_draw_point(dc, xCenter + y, yCenter - x);
		ptgui_dc_draw_point(dc, xCenter - y, yCenter - x);
	}
}
#else
void ptgui_dc_draw_circle(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y, rt_int16_t r)
{
	rt_int16_t cx = 0;
	rt_int16_t cy = r;
	rt_int16_t df = 1 - r;
	rt_int16_t d_e = 3;
	rt_int16_t d_se = -2 * r + 5;
	rt_int16_t xpcx,
			   xmcx,
			   xpcy,
			   xmcy;
	rt_int16_t ypcy,
			   ymcy,
			   ypcx,
			   ymcx;

	/*
	 * sanity check radius
	 */
	if (r < 0) {
		return ;
	}

	/* special case for r=0 - draw a point  */
	if (r == 0) {
		ptgui_dc_draw_point(dc, x, y);
	}

	/*
	 * draw circle
	 */
	do {
		ypcy = y + cy;
		ymcy = y - cy;
		if (cx > 0) {
			xpcx = x + cx;
			xmcx = x - cx;
			ptgui_dc_draw_point(dc, xmcx, ypcy);
			ptgui_dc_draw_point(dc, xpcx, ypcy);
			ptgui_dc_draw_point(dc, xmcx, ymcy);
			ptgui_dc_draw_point(dc, xpcx, ymcy);
		}
		else {
			ptgui_dc_draw_point(dc, x, ymcy);
			ptgui_dc_draw_point(dc, x, ypcy);
		}
		xpcy = x + cy;
		xmcy = x - cy;
		if ((cx > 0) && (cx != cy)) {
			ypcx = y + cx;
			ymcx = y - cx;
			ptgui_dc_draw_point(dc, xmcy, ypcx);
			ptgui_dc_draw_point(dc, xpcy, ypcx);
			ptgui_dc_draw_point(dc, xmcy, ymcx);
			ptgui_dc_draw_point(dc, xpcy, ymcx);
		}
		else if (cx == 0) {
			ptgui_dc_draw_point(dc, xmcy, y);
			ptgui_dc_draw_point(dc, xpcy, y);
		}

		/*
		   * Update
		   */
		if (df < 0) {
			df += d_e;
			d_e += 2;
			d_se += 2;
		}
		else {
			df += d_se;
			d_e += 2;
			d_se += 4;
			cy--;
		}
		cx++;
	}
	while (cx <= cy);
}
#endif

void ptgui_dc_fill_circle(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y, rt_int16_t r)
{
	rt_int16_t cx = 0;
	rt_int16_t cy = r;
	rt_int16_t ocx = (rt_int16_t)0xffff;
	rt_int16_t ocy = (rt_int16_t)0xffff;
	rt_int16_t df = 1 - r;
	rt_int16_t d_e = 3;
	rt_int16_t d_se = -2 * r + 5;
	rt_int16_t xpcx,
			   xmcx,
			   xpcy,
			   xmcy;
	rt_int16_t ypcy,
			   ymcy,
			   ypcx,
			   ymcx;

	/*
	 * Sanity check radius
	 */
	if (r < 0) {
		return;
	}

	/*
	 * Special case for r=0 - draw a point
	 */
	if (r == 0) {
		ptgui_dc_draw_point(dc, x, y);
		return ;
	}

	/*
	 * Draw
	 */
	do {
		xpcx = x + cx;
		xmcx = x - cx;
		xpcy = x + cy;
		xmcy = x - cy;
		if (ocy != cy) {
			if (cy > 0) {
				ypcy = y + cy;
				ymcy = y - cy;
				ptgui_dc_draw_hline(dc, xmcx, xpcx, ypcy);
				ptgui_dc_draw_hline(dc, xmcx, xpcx, ymcy);
			}
			else {
				ptgui_dc_draw_hline(dc, xmcx, xpcx, y);
			}
			ocy = cy;
		}
		if (ocx != cx) {
			if (cx != cy) {
				if (cx > 0) {
					ypcx = y + cx;
					ymcx = y - cx;
					ptgui_dc_draw_hline(dc, xmcy, xpcy, ymcx);
					ptgui_dc_draw_hline(dc, xmcy, xpcy, ypcx);
				}
				else {
					ptgui_dc_draw_hline(dc, xmcy, xpcy, y);
				}
			}
			ocx = cx;
		}
		/*
		 * Update
		 */
		if (df < 0) {
			df += d_e;
			d_e += 2;
			d_se += 2;
		}
		else {
			df += d_se;
			d_e += 2;
			d_se += 4;
			cy--;
		}
		cx++;
	}
	while (cx <= cy);
}

void ptgui_dc_draw_arc(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y, rt_int16_t r, rt_int16_t start, rt_int16_t end)
{
	rt_int16_t cx = 0;
	rt_int16_t cy = r;
	rt_int16_t df = 1 - r;
	rt_int16_t d_e = 3;
	rt_int16_t d_se = -2 * r + 5;
	rt_int16_t xpcx,
			   xmcx,
			   xpcy,
			   xmcy;
	rt_int16_t ypcy,
			   ymcy,
			   ypcx,
			   ymcx;
	rt_uint8_t drawoct;
	rt_int16_t startoct,
			   endoct,
			   oct,
			   stopval_start,
			   stopval_end;
	double temp;

	stopval_start = 0;
	stopval_end = 0;
	temp = 0;

	/* Sanity check radius */
	if (r < 0) {
		return ;
	}
	/* Special case for r=0 - draw a point */
	if (r == 0) {
		ptgui_dc_draw_point(dc, x, y);
		return;
	}

	/* Fixup angles */
	start = start % 360;
	end = end % 360;

	/*
	* Draw arc
	*/

	// Octant labelling
	//
	//  \ 5 | 6 /
	//   \  |  /
	//  4 \ | / 7
	//     \|/
	//------+------ +x
	//     /|\
	//  3 / | \ 0
	//   /  |  \
	//  / 2 | 1 \
	//  	+y

	drawoct = 0; // 0x00000000
	// whether or not to keep drawing a given octant.
	// For example: 0x00111100 means we're drawing in octants 2-5

	// 0 <= start & end < 360; note that sometimes start > end - if so, arc goes back through 0.
	while (start < 0)
		start += 360;
	while (end < 0)
		end += 360;
	start %= 360;
	end %= 360;

	// now, we find which octants we're drawing in.
	startoct = start / 45;
	endoct = end / 45;
	oct = startoct - 1; // we increment as first step in loop

	//stopval_start, stopval_end; // what values of cx to stop at.
	do {
		oct = (oct + 1) % 8;

		if (oct == startoct) {
			// need to compute stopval_start for this octant.  Look at picture above if this is unclear
			switch (oct) {
			case 0:
			case 3:
				temp = sin(start * M_PI / 180);
				break;
			case 1:
			case 6:
				temp = cos(start * M_PI / 180);
				break;
			case 2:
			case 5:
				temp = -cos(start * M_PI / 180);
				break;
			case 4:
			case 7:
				temp = -sin(start * M_PI / 180);
				break;
			}
			temp *= r;
			stopval_start = (rt_int16_t)temp; // always round down.
			// This isn't arbitrary, but requires graph paper to explain well.
			// The basic idea is that we're always changing drawoct after we draw, so we
			// stop immediately after we render the last sensible pixel at x = ((rt_int16_t)temp).

			// and whether to draw in this octant initially
			if (oct % 2) {
				drawoct |= (1 << oct);
			} // this is basically like saying drawoct[oct] = true, if drawoct were a bool array
			else {
				drawoct &= 255 - (1 << oct);
			} // this is basically like saying drawoct[oct] = false
		}

		if (oct == endoct) {
			// need to compute stopval_end for this octant
			switch (oct) {
			case 0:
			case 3:
				temp = sin(end * M_PI / 180);
				break;
			case 1:
			case 6:
				temp = cos(end * M_PI / 180);
				break;
			case 2:
			case 5:
				temp = -cos(end * M_PI / 180);
				break;
			case 4:
			case 7:
				temp = -sin(end * M_PI / 180);
				break;
			}
			temp *= r;
			stopval_end = (rt_int16_t)temp;

			// and whether to draw in this octant initially
			if (startoct == endoct) {
				// note:	  we start drawing, stop, then start again in this case
				// otherwise: we only draw in this octant, so initialize it to false, it will get set back to true
				if (start > end) {
					// unfortunately, if we're in the same octant and need to draw over the whole circle,
					// we need to set the rest to true, because the while loop will end at the bottom.
					drawoct = 255;
				}
				else {
					drawoct &= 255 - (1 << oct);
				}
			}
			else if (oct % 2) {
				drawoct &= 255 - (1 << oct);
			}
			else {
				drawoct |= (1 << oct);
			}
		}
		else if (oct != startoct) {
			// already verified that it's != endoct
			drawoct |= (1 << oct); // draw this entire segment
		}
	}
	while (oct != endoct);

	// so now we have what octants to draw and when to draw them.  all that's left is the actual raster code.
	do {
		ypcy = y + cy;
		ymcy = y - cy;
		if (cx > 0) {
			xpcx = x + cx;
			xmcx = x - cx;
			// always check if we're drawing a certain octant before adding a pixel to that octant.
			if (drawoct & 4) {
				ptgui_dc_draw_point(dc, xmcx, ypcy);
			} // drawoct & 4 = 22; drawoct[2]
			if (drawoct & 2) {
				ptgui_dc_draw_point(dc, xpcx, ypcy);
			}
			if (drawoct & 32) {
				ptgui_dc_draw_point(dc, xmcx, ymcy);
			}
			if (drawoct & 64) {
				ptgui_dc_draw_point(dc, xpcx, ymcy);
			}
		}
		else {
			if (drawoct & 6) {
				ptgui_dc_draw_point(dc, x, ypcy);
			} // 4 + 2; drawoct[2] || drawoct[1]
			if (drawoct & 96) {
				ptgui_dc_draw_point(dc, x, ymcy);
			} // 32 + 64
		}

		xpcy = x + cy;
		xmcy = x - cy;
		if (cx > 0 && cx != cy) {
			ypcx = y + cx;
			ymcx = y - cx;
			if (drawoct & 8) {
				ptgui_dc_draw_point(dc, xmcy, ypcx);
			}
			if (drawoct & 1) {
				ptgui_dc_draw_point(dc, xpcy, ypcx);
			}
			if (drawoct & 16) {
				ptgui_dc_draw_point(dc, xmcy, ymcx);
			}
			if (drawoct & 128) {
				ptgui_dc_draw_point(dc, xpcy, ymcx);
			}
		}
		else if (cx == 0) {
			if (drawoct & 24) {
				ptgui_dc_draw_point(dc, xmcy, y);
			} // 8 + 16
			if (drawoct & 129) {
				ptgui_dc_draw_point(dc, xpcy, y);
			} // 1 + 128
		}

		/*
			* Update whether we're drawing an octant
			*/
		if (stopval_start == cx) {
			// works like an on-off switch because start & end may be in the same octant.
			if (drawoct & (1 << startoct)) {
				drawoct &= 255 - (1 << startoct);
			}
			else {
				drawoct |= (1 << startoct);
			}
		}
		if (stopval_end == cx) {
			if (drawoct & (1 << endoct)) {
				drawoct &= 255 - (1 << endoct);
			}
			else {
				drawoct |= (1 << endoct);
			}
		}

		/*
			* Update pixels
			*/
		if (df < 0) {
			df += d_e;
			d_e += 2;
			d_se += 2;
		}
		else {
			df += d_se;
			d_e += 2;
			d_se += 4;
			cy--;
		}

		cx++;
	}
	while (cx <= cy);
}

void ptgui_dc_draw_ellipse(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y, rt_int16_t rx, rt_int16_t ry)
{
	rt_int16_t ix,
			   iy;
	rt_int16_t h,
			   i,
			   j,
			   k;
	rt_int16_t oh,
			   oi,
			   oj,
			   ok;
	rt_int16_t xmh,
			   xph,
			   ypk,
			   ymk;
	rt_int16_t xmi,
			   xpi,
			   ymj,
			   ypj;
	rt_int16_t xmj,
			   xpj,
			   ymi,
			   ypi;
	rt_int16_t xmk,
			   xpk,
			   ymh,
			   yph;

	/*
	 * Sanity check radii
	 */
	if ((rx < 0) || (ry < 0)) {
		return;
	}

	/*
	 * Special case for rx=0 - draw a vline
	 */
	if (rx == 0) {
		ptgui_dc_draw_vline(dc, x, y - ry, y + ry);
		return;
	}

	/*
	 * Special case for ry=0 - draw a hline
	 */
	if (ry == 0) {
		ptgui_dc_draw_hline(dc, x - rx, x + rx, y);
		return;
	}

	/*
	 * Init vars
	 */
	oh = oi = oj = ok = (rt_int16_t)0xFFFF;

	if (rx > ry) {
		ix = 0;
		iy = rx * 64;

		do {
			h = (ix + 32) >> 6;
			i = (iy + 32) >> 6;
			j = (h * ry) / rx;
			k = (i * ry) / rx;

			if (((ok != k) && (oj != k)) || ((oj != j) && (ok != j)) || (k != j)) {
				xph = x + h;
				xmh = x - h;
				if (k > 0) {
					ypk = y + k;
					ymk = y - k;
					ptgui_dc_draw_point(dc, xmh, ypk);
					ptgui_dc_draw_point(dc, xph, ypk);
					ptgui_dc_draw_point(dc, xmh, ymk);
					ptgui_dc_draw_point(dc, xph, ymk);
				}
				else {
					ptgui_dc_draw_point(dc, xmh, y);
					ptgui_dc_draw_point(dc, xph, y);
				}
				ok = k;
				xpi = x + i;
				xmi = x - i;
				if (j > 0) {
					ypj = y + j;
					ymj = y - j;
					ptgui_dc_draw_point(dc, xmi, ypj);
					ptgui_dc_draw_point(dc, xpi, ypj);
					ptgui_dc_draw_point(dc, xmi, ymj);
					ptgui_dc_draw_point(dc, xpi, ymj);
				}
				else {
					ptgui_dc_draw_point(dc, xmi, y);
					ptgui_dc_draw_point(dc, xpi, y);
				}
				oj = j;
			}

			ix = ix + iy / rx;
			iy = iy - ix / rx;
		}
		while (i > h);
	}
	else {
		ix = 0;
		iy = ry * 64;

		do {
			h = (ix + 32) >> 6;
			i = (iy + 32) >> 6;
			j = (h * rx) / ry;
			k = (i * rx) / ry;

			if (((oi != i) && (oh != i)) || ((oh != h) && (oi != h) && (i != h))) {
				xmj = x - j;
				xpj = x + j;
				if (i > 0) {
					ypi = y + i;
					ymi = y - i;
					ptgui_dc_draw_point(dc, xmj, ypi);
					ptgui_dc_draw_point(dc, xpj, ypi);
					ptgui_dc_draw_point(dc, xmj, ymi);
					ptgui_dc_draw_point(dc, xpj, ymi);
				}
				else {
					ptgui_dc_draw_point(dc, xmj, y);
					ptgui_dc_draw_point(dc, xpj, y);
				}
				oi = i;
				xmk = x - k;
				xpk = x + k;
				if (h > 0) {
					yph = y + h;
					ymh = y - h;
					ptgui_dc_draw_point(dc, xmk, yph);
					ptgui_dc_draw_point(dc, xpk, yph);
					ptgui_dc_draw_point(dc, xmk, ymh);
					ptgui_dc_draw_point(dc, xpk, ymh);
				}
				else {
					ptgui_dc_draw_point(dc, xmk, y);
					ptgui_dc_draw_point(dc, xpk, y);
				}
				oh = h;
			}

			ix = ix + iy / ry;
			iy = iy - ix / ry;
		}
		while (i > h);
	}
}

void ptgui_dc_fill_ellipse(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y, rt_int16_t rx, rt_int16_t ry)
{
	rt_int16_t ix,
			   iy;
	rt_int16_t h,
			   i,
			   j,
			   k;
	rt_int16_t oh,
			   oi,
			   oj,
			   ok;
	rt_int16_t xmh,
			   xph;
	rt_int16_t xmi,
			   xpi;
	rt_int16_t xmj,
			   xpj;
	rt_int16_t xmk,
			   xpk;

	/*
	 * Special case for rx=0 - draw a vline
	 */
	if (rx == 0) {
		ptgui_dc_draw_vline(dc, x, y - ry, y + ry);
		return;
	}

	/* special case for ry=0 - draw a hline  */
	if (ry == 0) {
		ptgui_dc_draw_hline(dc, x - rx, x + rx, y);
		return;
	}

	/*
	 * Init vars
	 */
	oh = oi = oj = ok = (rt_int16_t)0xFFFF;

	/*
	 * Draw
	 */
	if (rx > ry) {
		ix = 0;
		iy = rx * 64;

		do {
			h = (ix + 32) >> 6;
			i = (iy + 32) >> 6;
			j = (h * ry) / rx;
			k = (i * ry) / rx;

			if ((ok != k) && (oj != k)) {
				xph = x + h;
				xmh = x - h;
				if (k > 0) {
					ptgui_dc_draw_hline(dc, xmh, xph, y + k);
					ptgui_dc_draw_hline(dc, xmh, xph, y - k);
				}
				else {
					ptgui_dc_draw_hline(dc, xmh, xph, y);
				}
				ok = k;
			}
			if ((oj != j) && (ok != j) && (k != j)) {
				xmi = x - i;
				xpi = x + i;
				if (j > 0) {
					ptgui_dc_draw_hline(dc, xmi, xpi, y + j);
					ptgui_dc_draw_hline(dc, xmi, xpi, y - j);
				}
				else {
					ptgui_dc_draw_hline(dc, xmi, xpi, y);
				}
				oj = j;
			}

			ix = ix + iy / rx;
			iy = iy - ix / rx;
		}
		while (i > h);
	}
	else {
		ix = 0;
		iy = ry * 64;

		do {
			h = (ix + 32) >> 6;
			i = (iy + 32) >> 6;
			j = (h * rx) / ry;
			k = (i * rx) / ry;

			if ((oi != i) && (oh != i)) {
				xmj = x - j;
				xpj = x + j;
				if (i > 0) {
					ptgui_dc_draw_hline(dc, xmj, xpj, y + i);
					ptgui_dc_draw_hline(dc, xmj, xpj, y - i);
				}
				else {
					ptgui_dc_draw_hline(dc, xmj, xpj, y);
				}
				oi = i;
			}
			if ((oh != h) && (oi != h) && (i != h)) {
				xmk = x - k;
				xpk = x + k;
				if (h > 0) {
					ptgui_dc_draw_hline(dc, xmk, xpk, y + h);
					ptgui_dc_draw_hline(dc, xmk, xpk, y - h);
				}
				else {
					ptgui_dc_draw_hline(dc, xmk, xpk, y);
				}
				oh = h;
			}
			ix = ix + iy / ry;
			iy = iy - ix / ry;
		}
		while (i > h);
	}
}

void ptgui_dc_draw_focus_rect(struct ptgui_dc *dc, ptgui_rect_t *rect)
{
	rt_int16_t i;

	for (i = rect->x1; i <= rect->x2; i += 2) {
		ptgui_dc_draw_point(dc, i, rect->y1);
		ptgui_dc_draw_point(dc, i, rect->y2);
	}

	for (i = rect->y1; i <= rect->y2; i += 2) {
		ptgui_dc_draw_point(dc, rect->x1, i);
		ptgui_dc_draw_point(dc, rect->x2, i);
	}
}

void ptgui_dc_get_rect(struct ptgui_dc *dc, ptgui_rect_t *rect)
{
	if (dc != RT_NULL && rect != RT_NULL) {
		dc->get_rect(dc, rect);
	}
}
